package q1.project.modules.system.menu.service;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Page;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import q1.project.common.context.TenantContext;
import q1.project.common.enums.EnableStatusEnum;
import q1.project.core.exception.BusinessException;
import q1.project.core.jpa.WhereBuilderNow;
import q1.project.modules.system.role.dao.MenuDao;
import q1.project.modules.system.role.entity.MenuEntity;
import q1.project.modules.system.role.model.MenuVm;
import q1.project.modules.system.role.service.UserRoleService;

/**
 * 菜单服务实现
 *
 * @author Q1 Team
 * @version 1.0
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class MenuService {

  private final MenuDao menuDao;
  private final UserRoleService userRoleService;

  @Transactional(rollbackFor = Exception.class)
  public MenuVm.MenuResponse createMenu(MenuEntity menu) {
    log.info("创建菜单: menuCode={}", menu.getMenuCode());

    // 校验菜单编码是否已存在
    if (menuDao.findByMenuCode(menu.getMenuCode()).isPresent()) {
      log.warn("菜单编码已存在: {}", menu.getMenuCode());
      throw new BusinessException("菜单编码已存在");
    }

    // 如果设置了父菜单，校验父菜单是否存在
    if (menu.getParentId() != null && !menu.getParentId().isEmpty()) {
      menuDao.findById(menu.getParentId()).orElseThrow(() -> new BusinessException("父菜单不存在"));
    }

    menu = menuDao.save(menu);
    log.info("菜单创建成功: id={}, menuCode={}", menu.getId(), menu.getMenuCode());

    return convertToResponse(menu);
  }

  @Transactional(rollbackFor = Exception.class)
  public MenuVm.MenuResponse updateMenu(MenuEntity menu) {
    log.info("更新菜单: id={}", menu.getId());

    MenuEntity existingMenu =
        menuDao.findById(menu.getId()).orElseThrow(() -> new BusinessException("菜单不存在"));

    // 检查是否尝试将菜单设置为自己的子菜单（防止循环引用）
    if (menu.getParentId() != null && menu.getParentId().equals(menu.getId())) {
      throw new BusinessException("不能将菜单设置为自己的子菜单");
    }

    // 如果设置了父菜单，校验父菜单是否存在
    if (menu.getParentId() != null && !menu.getParentId().isEmpty()) {
      menuDao.findById(menu.getParentId()).orElseThrow(() -> new BusinessException("父菜单不存在"));
    }

    // 更新基本信息
    existingMenu.setMenuName(menu.getMenuName());
    existingMenu.setParentId(menu.getParentId());
    existingMenu.setMenuType(menu.getMenuType());
    existingMenu.setPath(menu.getPath());
    existingMenu.setComponent(menu.getComponent());
    existingMenu.setIcon(menu.getIcon());
    existingMenu.setSortOrder(menu.getSortOrder());
    existingMenu.setRemark(menu.getRemark());
    existingMenu.setStatus(menu.getStatus());

    existingMenu = menuDao.save(existingMenu);
    log.info("菜单更新成功: id={}, menuCode={}", existingMenu.getId(), existingMenu.getMenuCode());

    return convertToResponse(existingMenu);
  }

  @Transactional(rollbackFor = Exception.class)
  public void deleteMenu(String id) {
    log.info("删除菜单: id={}", id);

    MenuEntity menu = menuDao.findById(id).orElseThrow(() -> new BusinessException("菜单不存在"));

    // 检查是否有子菜单
    List<MenuEntity> children = menuDao.findByParentIdOrderBySortOrderAsc(id);
    if (!children.isEmpty()) {
      throw new BusinessException("该菜单下有子菜单，无法删除");
    }

    menuDao.delete(menu);

    log.info("菜单删除成功: id={}, menuCode={}", id, menu.getMenuCode());
  }

  public MenuVm.MenuResponse getMenuById(String id) {
    log.debug("查询菜单: id={}", id);

    MenuEntity menu = menuDao.findById(id).orElseThrow(() -> new BusinessException("菜单不存在"));

    return convertToResponse(menu);
  }

  public MenuVm.MenuResponse getMenuByCode(String menuCode) {
    log.debug("根据菜单编码查询菜单: menuCode={}", menuCode);

    MenuEntity menu =
        menuDao.findByMenuCode(menuCode).orElseThrow(() -> new BusinessException("菜单不存在"));

    return convertToResponse(menu);
  }

  public Page<MenuVm.MenuResponse> listMenus(MenuVm.MenuQuery query) {
    log.debug(
        "分页查询菜单: page={}, size={}, keyword={}, menuType={}",
        query.getPage(),
        query.getSize(),
        query.getKeyword(),
        query.getMenuType());

    query.validate();
    String tenantId = TenantContext.getTenantId();

    // 使用WhereBuilderNow构建动态查询条件
    WhereBuilderNow<MenuEntity> builder = new WhereBuilderNow<>();
    builder.init(query);

    Specification<MenuEntity> spec =
        builder.build(
            w -> {
              // 租户ID条件（必须）
              w.stringEqual("tenantId", tenantId);

              // 菜单类型精确查询
              if (query.getMenuType() != null) {
                w.intEqual("menuType", query.getMenuType());
              }

              // 关键词搜索（如果有关键词，在多个字段中搜索）
              if (StringUtils.hasText(query.getKeyword())) {
                w.keywordSearch(query.getKeyword(), "menuCode", "menuName", "path", "component");
              }

              // 排序：按排序字段升序，然后按创建时间降序
              w.ascOrder("sortOrder");
              w.descOrder("createTime");
            });

    // 执行查询
    Page<MenuEntity> page = menuDao.findAll(spec, builder.getPageable());

    return page.map(this::convertToResponse);
  }

  public List<MenuVm.MenuResponse> listEnabledMenuTree() {
    log.debug("查询所有启用的菜单树");

    List<MenuEntity> menus = menuDao.findByStatusOrderBySortOrderAsc(EnableStatusEnum.ACTIVE);

    return buildMenuTree(menus.stream().map(this::convertToResponse).collect(Collectors.toList()));
  }

  public List<MenuVm.MenuResponse> listAllMenuTree() {
    log.debug("查询所有菜单树");

    String tenantId = TenantContext.getTenantId();
    List<MenuEntity> menus = menuDao.findByTenantIdOrderBySortOrderAsc(tenantId);

    return buildMenuTree(menus.stream().map(this::convertToResponse).collect(Collectors.toList()));
  }

  public List<MenuVm.MenuResponse> getCurrentUserMenus() {
    log.debug("查询当前用户菜单树");

    // TODO: 从 JWT Token 获取用户ID和角色
    String userId = null; // getCurrentUserId();
    String tenantId = TenantContext.getTenantId();

    // TODO: 检查是否是超管
    boolean isSuperAdmin = false; // hasRole("SUPER_ADMIN");

    if (isSuperAdmin) {
      // 超管返回所有启用的菜单
      log.debug("当前用户是超管，返回所有启用的菜单");
      return listEnabledMenuTree();
    }

    // 普通用户：返回有权限的菜单（通过角色-菜单关联查询）
    List<MenuEntity> menus = userRoleService.findMenusByUserId(userId);

    if (menus == null || menus.isEmpty()) {
      log.warn("用户没有菜单权限: userId={}", userId);
      return new ArrayList<>();
    }

    // 构建菜单树（已过滤出启用的菜单）
    return buildMenuTree(menus.stream().map(this::convertToResponse).collect(Collectors.toList()));
  }

  /** 构建菜单树 */
  private List<MenuVm.MenuResponse> buildMenuTree(List<MenuVm.MenuResponse> menuList) {
    // 按父ID分组
    Map<String, List<MenuVm.MenuResponse>> menuMap =
        menuList.stream()
            .filter(menu -> menu.getParentId() != null && !menu.getParentId().isEmpty())
            .collect(Collectors.groupingBy(MenuVm.MenuResponse::getParentId));

    // 构建树结构
    List<MenuVm.MenuResponse> rootMenus = new ArrayList<>();
    for (MenuVm.MenuResponse menu : menuList) {
      if (menu.getParentId() == null || menu.getParentId().isEmpty()) {
        rootMenus.add(menu);
      }
      List<MenuVm.MenuResponse> children = menuMap.get(menu.getId());
      if (children != null && !children.isEmpty()) {
        menu.setChildren(children);
      }
    }

    return rootMenus;
  }

  /** 转换为VO对象 */
  private MenuVm.MenuResponse convertToResponse(MenuEntity entity) {
    MenuVm.MenuResponse vo = new MenuVm.MenuResponse();
    vo.setId(entity.getId());
    vo.setParentId(entity.getParentId());
    vo.setMenuCode(entity.getMenuCode());
    vo.setMenuName(entity.getMenuName());
    vo.setMenuType(entity.getMenuType());
    vo.setPath(entity.getPath());
    vo.setComponent(entity.getComponent());
    vo.setIcon(entity.getIcon());
    vo.setSortOrder(entity.getSortOrder());
    vo.setRemark(entity.getRemark());
    vo.setEnabled(entity.getStatus() == EnableStatusEnum.ACTIVE);
    vo.setCreateTime(entity.getCreateTime());
    vo.setUpdateTime(entity.getUpdateTime());
    return vo;
  }
}
